package org.andao.commons.lucene.search;

import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.andao.commons.lucene.bean.ConditionBean;
import org.andao.commons.lucene.core.LuceneServerFactory;
import org.andao.commons.lucene.core.SolrServerContext;
import org.andao.commons.lucene.core.extractor.DocumentListExtractor;
import org.andao.commons.lucene.core.extractor.SolrDocumentListExtractor;
import org.andao.commons.lucene.core.mapper.IndexMapper;
import org.andao.commons.lucene.exception.LuceneQueryException;
import org.andao.core.base.controller.BaseSupportController;
import org.andao.core.dao.helper.OrderBy;
import org.andao.core.dao.helper.Page;
import org.andao.core.utils.ExCollectionUtils;
import org.andao.core.utils.ExStringUtils;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.ORDER;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocumentList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


/**
 * lucene搜索器，基于solr的实现
 * 
 * @author Seewo Software - Vick.liu (liuguanglin@cvte.cn)
 * 
 */
@Component("solrSearcher")
public class SolrSearcher implements LuceneSearcher {

	protected static Logger logger = LoggerFactory.getLogger(BaseSupportController.class);

	private SolrQuery solrQuery;

	private SolrServer solrServer;


	public SolrSearcher() throws LuceneQueryException {
			solrServer = (SolrServer) LuceneServerFactory.getServer(new SolrServerContext());
	}

	/**
	 * 检索
	 * 
	 * @param page
	 *            结果分页
	 * @param conditionBeanList
	 *            搜索条件，必须有一个isFilter=false
	 * @param mapper
	 *            结果包装器
	 * @return
	 * @throws LuceneQueryException
	 * @throws SolrServerException
	 */
	@Override
	public Page query(Page page, List<ConditionBean> conditionBeanList, IndexMapper mapper) throws LuceneQueryException, SolrServerException {
		return query(page, conditionBeanList, new SolrDocumentListExtractor(mapper));
	}

	/**
	 * 检索
	 * 
	 * @param page
	 *            结果分页
	 * @param conditionBeanList
	 *            搜索条件，必须有一个isFilter=false
	 * @param listExtractor
	 *            结果分解器
	 * @return
	 * @throws LuceneQueryException
	 * @throws SolrServerException
	 */
	@Override
	public Page query(Page page, List<ConditionBean> conditionBeanList, DocumentListExtractor listExtractor) throws LuceneQueryException, SolrServerException {

		ConditionBean fuzzyConditionBean = createSolrQuery(conditionBeanList);// 创建检索条件
		setResultOrder(solrQuery, page.getOrderList());// 设置排序
		if (ExStringUtils.isNotBlank(fuzzyConditionBean.getKeywords())) {// 如果有关键词，则设置高亮
			setHighlightProps(solrQuery, fuzzyConditionBean.getMatchingIndex());
		}

		solrQuery.setStart((page.getPageNum() - 1) * page.getPageSize()); // start
		solrQuery.setRows(page.getPageSize()); // page per size row

		QueryResponse queryResponse = solrServer.query(solrQuery);
		SolrDocumentList documentList = queryResponse.getResults();
		page.setTotalCount((int) documentList.getNumFound());

		Map<String, Map<String, List<String>>> highlightMap = queryResponse.getHighlighting(); // 获取高亮
		List<?> results = (List<?>) listExtractor.extractData(documentList, highlightMap);
		page.setResult(results);

		return page;
	}

	/********************* 以下为辅助方法 *************/
	/**
	 * 创建检索条件
	 * 
	 * @param conditionBeanList
	 *            搜索条件bean（包括模糊匹配的query和filter）
	 * @return 模糊匹配的bean
	 * @throws LuceneQueryException
	 */
	public ConditionBean createSolrQuery(List<ConditionBean> conditionBeanList) throws LuceneQueryException {
		this.solrQuery = new SolrQuery();
		StringBuilder paramter = new StringBuilder();
		ConditionBean fuzzyConditionBean = null;
		for (ConditionBean cBean : conditionBeanList) {
			String keywords = fixBlankKeyword(transformSolrMetacharactor(cBean.getKeywords()).trim());
			if (!cBean.isFilter()) {
				if (null != fuzzyConditionBean) {
					// 如果已经有query了，则抛异常
					throw new LuceneQueryException("Only one fuzzyQuery is acceptable.Other conditionBean should be declared as query filter");
				}
				//遍历每一个匹配索引，构造query
				for (int i = 0; i < cBean.getMatchingIndex().length; i++) {
					if (i > 0) {
						paramter.append(" OR ");
					}
					paramter.append(cBean.getMatchingIndex()[i]);
					paramter.append(keywords);
				}//end for
				// 设置query
				solrQuery.setQuery(paramter.toString());
				fuzzyConditionBean = cBean;
			} else {
				try {
					solrQuery.addFilterQuery(cBean.getMatchingIndex()[0] + ":" + cBean.getKeywords());
				} catch (Exception e) {
					// catch out of index exception
					logger.debug("one matchingIndex should be fill.");
					throw new LuceneQueryException("one matchingIndex should be fill.");
				}
			}
		}
		if (null == fuzzyConditionBean) {
			throw new LuceneQueryException("FuzzyQuery is required!(ConditionBean whose isFilter = false)");
		}
		return fuzzyConditionBean;
	}

	/**
	 * 排序
	 * 
	 * @param page
	 * @param solrQuery
	 */
	public void setResultOrder(SolrQuery solrQuery, List<OrderBy> orderList) {
		if (ExCollectionUtils.isNotEmpty(orderList)) {
			for (OrderBy ob : orderList) {
				solrQuery.addSortField(ob.getOrderBy(), (ob.getOrder().equalsIgnoreCase(Page.DESC)) ? ORDER.desc : ORDER.asc);
			}
		}
	}

	/**
	 * 设置高亮，默认使用conditionBean中模糊匹配字段作为高亮字段
	 * 
	 * @param solrQuery
	 * @param higlightFields
	 *            要高亮的字段
	 */
	public void setHighlightProps(SolrQuery solrQuery, String[] higlightFields) {
		setHighlightProps(solrQuery, higlightFields, false);
	}

	/**
	 * 设置高亮，默认使用conditionBean中模糊匹配字段作为高亮字段
	 * 
	 * @param solrQuery
	 * @param higlightFields
	 *            要高亮的字段
	 * @param isCustomStyle
	 *            是否自定义样式（true:是）
	 */
	public void setHighlightProps(SolrQuery solrQuery, String[] higlightFields, boolean isCustomStyle) {
		solrQuery.setHighlight(true);
		for (String field : higlightFields) {
			solrQuery.addHighlightField(field);
		}

		// 如果不自定义样式，则使用默认样式
		if (!isCustomStyle) {
			solrQuery.setHighlightSimplePre("<font color='red'>");// 标记，高亮关键字前缀
			solrQuery.setHighlightSimplePost("</font>");// 后缀
			solrQuery.setHighlightSnippets(2);// 结果分片数，默认为1
			solrQuery.setHighlightFragsize(100);// 每个分片的最大长度，默认为100
		}
	}

	/**
	 * 获取solrQuery
	 * 
	 * @return
	 */
	public SolrQuery getSolrQuery() {
		return solrQuery;
	}

	/**************** private method ************/
	/**
	 * 处理null或者""," "字符串
	 * 
	 * @param keywords
	 * @return
	 */
	private String fixBlankKeyword(String keywords) {
		if (ExStringUtils.isBlank(keywords)) {
			return ":*";
		} else {
			return ":" + keywords;
		}
	}

	/**
	 * 特殊字符转义 + – && || ! ( ) { } [ ] ^ ” ~ * ? : \
	 * 
	 * @param keywords
	 * @return
	 */
	public String transformSolrMetacharactor(String keywords) {
		keywords = keywords == null ? "" : keywords;// 防止空
		StringBuffer sb = new StringBuffer();
		String regex = "[+\\-&|!(){}\\[\\]^\"~*?:(\\)]";
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(keywords);
		while (matcher.find()) {
			matcher.appendReplacement(sb, "\\\\" + matcher.group());
		}
		matcher.appendTail(sb);
		return sb.toString();
	}

}
